package common

import (
	"regexp"
	"strings"
	"sync"
)

type tTokens struct {
	cache   map[string]*regexp.Regexp
	rwmutex *sync.RWMutex
}

var Tokens = newTokensLib()

func newTokensLib() *tTokens {
	it := new(tTokens)
	it.init()
	return it
}

func (me *tTokens) init() {
	me.cache = make(map[string]*regexp.Regexp)
	me.rwmutex = new(sync.RWMutex)
}

func (me *tTokens) MatchString(s string, p string) bool {
	return strings.HasPrefix(s, p)
}

func (me *tTokens) MatchRegexp(s string, p string) (bool, string) {
	me.rwmutex.RLock()
	r, ok := me.cache[p]
	me.rwmutex.RUnlock()

	if !ok {
		me.rwmutex.Lock()
		if r, ok = me.cache[p]; !ok {
			r, _ = regexp.Compile(p)
		}
		me.rwmutex.Unlock()
	}

	if r == nil {
		return false, ""
	}

	if !r.MatchString(s) {
		return false, ""
	}

	return true, r.FindString(s)
}

func (me *tTokens) MatchIdentifier(s string) (bool, string) {
	return me.MatchRegexp(s, "^[_a-zA-Z]\\w{0,99}")
}

func (me *tTokens) MatchSpaces(s string) (bool, string) {
	return me.MatchRegexp(s, `^\s+`)
}

func (me *tTokens) MatchDir(s string) (bool, string) {
	b, s := me.MatchRegexp(s, "^([a-zA-Z]\\:)?([\\\\/][^\\s/:*?<>|\\\"\\\\]+)+[\\/]?")
	if b {
		return b, s
	}

	b, s = me.MatchRegexp(s, "^\\\"([a-zA-Z]\\:)?([\\\\/][^/:*?<>|\\\"\\\\]+)+[\\/]?\\\"")
	if b {
		return b, s
	}

	b, s = me.MatchRegexp(s, "^'([a-zA-Z]\\:)?([\\\\/][^'/:*?<>|\\\"\\\\]+)+[\\/]?'")
	if b {
		return b, s
	}

	return false, ""
}

func (me *tTokens) MatchDataType(s string) (bool, string) {
	if ok, t := me.MatchBasicType(s); ok {
		return true, t
	}

	if ok, t := me.MatchCustomType(s); ok {
		return true, t
	}

	if ok, t := me.MatchPointerType(s); ok {
		return true, t
	}

	if ok, t := me.MatchArrayType(s); ok {
		return true, t
	}

	if ok, t := me.MatchMapType(s); ok {
		return true, t
	}

	return false, ""
}

func (me *tTokens) MatchBasicType(s string) (bool, string) {
	list := []string{
		"int",
		"string",
		"bool",
		"byte",
		"int32",
		"int64",
		"uint32",
		"uint64",
		"float32",
		"float64",
		"int8",
		"uint8",
		"int16",
		"uint16",
		`time\.Time`,
	}

	for _, it := range list {
		if ok, t := me.MatchRegexp(s, "^"+it+`(\s+|$)`); ok {
			return true, strings.TrimSpace(t)
		}
	}

	return false, ""
}

func (me *tTokens) MatchCustomType(s string) (bool, string) {
	t := s
	b1, s1 := me.MatchRegexp(t, `^\w+\.`)
	if b1 {
		t = t[len(s1):]
	}

	b2, s2 := me.MatchRegexp(t, `^\w+`)
	if !b2 {
		return false, ""
	}
	if s2 == "map" {
		// map is reserved word
		return false, ""
	}

	return true, s1 + s2
}

func (me *tTokens) MatchPointerType(s string) (bool, string) {
	t := s
	if t[0] != '*' {
		return false, ""
	}
	t = t[1:]

	b, s := me.MatchDataType(t)
	if !b {
		return false, ""
	}

	return true, "*" + s
}

func (me *tTokens) MatchArrayType(s string) (bool, string) {
	t := s
	b1, s1 := me.MatchRegexp(s, `^\[\s*\d*\s*\]\s*`)
	if !b1 {
		return false, ""
	}
	t = t[len(s1):]

	b2, s2 := me.MatchDataType(t)
	if !b2 {
		return false, ""
	}
	return true, s1 + s2
}

func (me *tTokens) MatchMapType(s string) (bool, string) {
	t := s
	s1 := "map"
	if !me.MatchString(t, s1) {
		return false, ""
	}
	t = t[len(s1):]

	b2, s2 := me.MatchRegexp(t, `^\s*\[\s*`)
	if !b2 {
		return false, ""
	}
	t = t[len(s2):]

	b3, s3 := me.MatchDataType(t)
	if !b3 {
		return false, ""
	}
	t = t[len(s3):]

	b4, s4 := me.MatchRegexp(t, `^\s*\]\s*`)
	if !b4 {
		return false, ""
	}
	t = t[len(s4):]

	b5, s5 := me.MatchDataType(t)
	if !b5 {
		return false, ""
	}

	return true, s1 + s2 + s3 + s4 + s5
}
